// import MapboxDraw from '@mapbox/mapbox-gl-draw';
// import DrawRectangle from 'mapbox-gl-draw-rectangle-mode';
// import * as turf from '@turf/turf';

const HALF_PI = Math.PI / 2;
const TWO_PI = Math.PI * 2;
const ZERO_TOLERANCE = 0.0001;
const FITTING_COUNT = 100;

// 是否是顺时针
function isClockWise(pnt1, pnt2, pnt3) {
	return (
		(pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) >
		(pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0])
	);
}

// 计算两点中点
function calcMid(pnt1, pnt2) {
	return [(pnt1[0] + pnt2[0]) / 2, (pnt1[1] + pnt2[1]) / 2];
}

// 计算两点之间的距离
function calcDistance(pnt1, pnt2) {
	return Math.sqrt(
		Math.pow(pnt1[0] - pnt2[0], 2) + Math.pow(pnt1[1] - pnt2[1], 2),
	);
}

// 获取基本长度
function getBaseLength(points) {
	return Math.pow(wholeDistance(points), 0.99);
}

// 计算整体距离
function wholeDistance(points) {
	let distance = 0;
	for (let i = 0; i < points.length - 1; i++)
		distance += calcDistance(points[i], points[i + 1]);
	return distance;
}

// 计算QBSpline曲线
function getQBSplinePoints(points) {
	if (points.length <= 2) return points;

	const n = 2;

	const bSplinePoints = [];
	const m = points.length - n - 1;
	bSplinePoints.push(points[0]);

	for (let i = 0; i <= m; i++) {
		for (let t = 0; t <= 1; t += 0.05) {
			let y = 0;
			let x = 0;
			for (let k = 0; k <= n; k++) {
				const factor = getQuadricBSplineFactor(k, t);
				x += factor * points[i + k][0];
				y += factor * points[i + k][1];
			}
			bSplinePoints.push([x, y]);
		}
	}
	bSplinePoints.push(points[points.length - 1]);
	return bSplinePoints;
}

// 计算二次贝塞尔曲线的系数
function getQuadricBSplineFactor(k, t) {
	if (k == 0) return Math.pow(t - 1, 2) / 2;
	if (k == 1) return (-2 * Math.pow(t, 2) + 2 * t + 1) / 2;
	if (k == 2) return Math.pow(t, 2) / 2;
	return 0;
}

// 计算第三点
function getThirdPoint(startPnt, endPnt, angle, distance, clockWise) {
	const azimuth = getAzimuth(startPnt, endPnt);
	const alpha = clockWise ? azimuth + angle : azimuth - angle;
	const dx = distance * Math.cos(alpha);
	const dy = distance * Math.sin(alpha);

	return [endPnt[0] + dx, endPnt[1] + dy];
}

// 计算对称点
function getTempPoint4(linePnt1, linePnt2, point) {
	const midPnt = calcMid(linePnt1, linePnt2);
	const len = calcDistance(midPnt, point);
	const angle = getAngleOfThreePoints(linePnt1, midPnt, point);
	let symPnt, distance1, distance2, mid;

	if (angle < HALF_PI) {
		distance1 = len * Math.sin(angle);
		distance2 = len * Math.cos(angle);
		mid = getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, false);
		symPnt = getThirdPoint(midPnt, mid, HALF_PI, distance2, true);
	} else if (angle >= HALF_PI && angle < Math.PI) {
		distance1 = len * Math.sin(Math.PI - angle);
		distance2 = len * Math.cos(Math.PI - angle);
		mid = getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, false);
		symPnt = getThirdPoint(midPnt, mid, HALF_PI, distance2, false);
	} else if (angle >= Math.PI && angle < Math.PI * 1.5) {
		distance1 = len * Math.sin(angle - Math.PI);
		distance2 = len * Math.cos(angle - Math.PI);
		mid = getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, true);
		symPnt = getThirdPoint(midPnt, mid, HALF_PI, distance2, true);
	} else {
		distance1 = len * Math.sin(Math.PI * 2 - angle);
		distance2 = len * Math.cos(Math.PI * 2 - angle);
		mid = getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, true);
		symPnt = getThirdPoint(midPnt, mid, HALF_PI, distance2, false);
	}

	return symPnt;
}

// 计算三点之间的角度
function getAngleOfThreePoints(pntA, pntB, pntC) {
	const angle = getAzimuth(pntB, pntA) - getAzimuth(pntB, pntC);
	return angle < 0 ? angle + TWO_PI : angle;
}

// 计算两点之间的角度
function getAzimuth(startPnt, endPnt) {
	let azimuth;

	const angle = Math.asin(
		Math.abs(endPnt[1] - startPnt[1]) / calcDistance(startPnt, endPnt),
	);

	if (endPnt[1] >= startPnt[1] && endPnt[0] >= startPnt[0])
		azimuth = angle + Math.PI;
	else if (endPnt[1] >= startPnt[1] && endPnt[0] < startPnt[0])
		azimuth = TWO_PI - angle;
	else if (endPnt[1] < startPnt[1] && endPnt[0] < startPnt[0]) azimuth = angle;
	else if (endPnt[1] < startPnt[1] && endPnt[0] >= startPnt[0])
		azimuth = Math.PI - angle;

	return azimuth;
}

// 贝塞尔曲线
function getBezierPoints(points) {
	if (points.length <= 2) return points;

	const bezierPoints = [];
	const n = points.length - 1;
	for (let t = 0; t <= 1; t += 0.01) {
		let y = 0;
		let x = 0;
		for (let index = 0; index <= n; index++) {
			const factor = getBinomialFactor(n, index);
			const a = Math.pow(t, index);
			const b = Math.pow(1 - t, n - index);
			x += factor * a * b * points[index][0];
			y += factor * a * b * points[index][1];
		}
		bezierPoints.push([x, y]);
	}
	bezierPoints.push(points[n]);
	return bezierPoints;
}

// 计算二项式系数
function getBinomialFactor(n, index) {
	return getFactorial(n) / (getFactorial(index) * getFactorial(n - index));
}

// 计算阶乘
function getFactorial(n) {
	if (n <= 1) return 1;
	if (n == 2) return 2;
	if (n == 3) return 6;
	if (n == 4) return 24;
	if (n == 5) return 120;

	let result = 1;
	for (let i = 1; i <= n; i++) result *= i;
	return result;
}

function getBisectorNormals(t, pnt1, pnt2, pnt3) {
	const normal = getNormal(pnt1, pnt2, pnt3);
	const dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]);
	const uX = normal[0] / dist;
	const uY = normal[1] / dist;
	const d1 = calcDistance(pnt1, pnt2);
	const d2 = calcDistance(pnt2, pnt3);
	let dt, x, y;
	let bisectorNormalRight = [];
	let bisectorNormalLeft = [];
	if (dist > ZERO_TOLERANCE) {
		if (isClockWise(pnt1, pnt2, pnt3)) {
			dt = t * d1;
			x = pnt2[0] - dt * uY;
			y = pnt2[1] + dt * uX;
			bisectorNormalRight = [x, y];

			dt = t * d2;
			x = pnt2[0] + dt * uY;
			y = pnt2[1] - dt * uX;
			bisectorNormalLeft = [x, y];
		} else {
			dt = t * d1;
			x = pnt2[0] + dt * uY;
			y = pnt2[1] - dt * uX;
			bisectorNormalRight = [x, y];

			dt = t * d2;
			x = pnt2[0] - dt * uY;
			y = pnt2[1] + dt * uX;
			bisectorNormalLeft = [x, y];
		}
	} else {
		x = pnt2[0] + t * (pnt1[0] - pnt2[0]);
		y = pnt2[1] + t * (pnt1[1] - pnt2[1]);
		bisectorNormalRight = [x, y];
		x = pnt2[0] + t * (pnt3[0] - pnt2[0]);
		y = pnt2[1] + t * (pnt3[1] - pnt2[1]);
		bisectorNormalLeft = [x, y];
	}
	return [bisectorNormalRight, bisectorNormalLeft];
}

function getNormal(pnt1, pnt2, pnt3) {
	let dX1 = pnt1[0] - pnt2[0];
	let dY1 = pnt1[1] - pnt2[1];
	const d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1);
	dX1 /= d1;
	dY1 /= d1;

	let dX2 = pnt3[0] - pnt2[0];
	let dY2 = pnt3[1] - pnt2[1];
	const d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2);
	dX2 /= d2;
	dY2 /= d2;

	const uX = dX1 + dX2;
	const uY = dY1 + dY2;
	return [uX, uY];
}

function getCubicValue(temp, startPnt, cPnt1, cPnt2, endPnt) {
	const t = Math.max(Math.min(temp, 1), 0);
	const tp = 1 - t;
	const t2 = t * t;
	const t3 = t2 * t;
	const tp2 = tp * tp;
	const tp3 = tp2 * tp;
	const x =
		tp3 * startPnt[0] +
		3 * tp2 * t * cPnt1[0] +
		3 * tp * t2 * cPnt2[0] +
		t3 * endPnt[0];
	const y =
		tp3 * startPnt[1] +
		3 * tp2 * t * cPnt1[1] +
		3 * tp * t2 * cPnt2[1] +
		t3 * endPnt[1];
	return [x, y];
}

class AttackArrow {
	static headHeightFactor = 0.18;
	static headWidthFactor = 0.3;
	static neckHeightFactor = 0.85;
	static neckWidthFactor = 0.15;
	static headTailFactor = 0.8;

	constructor() {}

	static getArrowHeadPoints(points, tailLeft, tailRight) {
		let len = getBaseLength(points);
		let headHeight = len * this.headHeightFactor;
		const headPnt = points[points.length - 1];
		len = calcDistance(headPnt, points[points.length - 2]);
		const tailWidth = calcDistance(tailLeft, tailRight);

		if (headHeight > tailWidth * this.headTailFactor) {
			headHeight = tailWidth * this.headTailFactor;
		}

		const headWidth = headHeight * this.headWidthFactor;
		const neckWidth = headHeight * this.neckWidthFactor;
		headHeight = headHeight > len ? len : headHeight;
		const neckHeight = headHeight * this.neckHeightFactor;

		const headEndPnt = getThirdPoint(
			points[points.length - 2],
			headPnt,
			0,
			headHeight,
			true,
		);
		const neckEndPnt = getThirdPoint(
			points[points.length - 2],
			headPnt,
			0,
			neckHeight,
			true,
		);
		const headLeft = getThirdPoint(
			headPnt,
			headEndPnt,
			HALF_PI,
			headWidth,
			false,
		);
		const headRight = getThirdPoint(
			headPnt,
			headEndPnt,
			HALF_PI,
			headWidth,
			true,
		);
		const neckLeft = getThirdPoint(
			headPnt,
			neckEndPnt,
			HALF_PI,
			neckWidth,
			false,
		);
		const neckRight = getThirdPoint(
			headPnt,
			neckEndPnt,
			HALF_PI,
			neckWidth,
			true,
		);
		return [neckLeft, headLeft, headPnt, headRight, neckRight];
	}

	static getArrowBodyPoints(points, neckLeft, neckRight, tailWidthFactor) {
		const allLen = wholeDistance(points);
		const len = getBaseLength(points);
		const tailWidth = len * tailWidthFactor;
		const neckWidth = calcDistance(neckLeft, neckRight);
		const widthDif = (tailWidth - neckWidth) / 2;
		let tempLen = 0,
			leftBodyPnts = [],
			rightBodyPnts = [];
		for (let i = 1; i < points.length - 1; i++) {
			const angle =
				getAngleOfThreePoints(points[i - 1], points[i], points[i + 1]) / 2;
			tempLen += calcDistance(points[i - 1], points[i]);
			const w =
				(tailWidth / 2 - (tempLen / allLen) * widthDif) / Math.sin(angle);
			const left = getThirdPoint(
				points[i - 1],
				points[i],
				Math.PI - angle,
				w,
				true,
			);
			const right = getThirdPoint(points[i - 1], points[i], angle, w, false);
			leftBodyPnts.push(left);
			rightBodyPnts.push(right);
		}
		return leftBodyPnts.concat(rightBodyPnts);
	}

	static generate(pnts) {
		// 计算箭尾
		let tailLeft = pnts[0];
		let tailRight = pnts[1];
		if (isClockWise(pnts[0], pnts[1], pnts[2])) {
			tailLeft = pnts[1];
			tailRight = pnts[0];
		}
		const midTail = calcMid(tailLeft, tailRight);
		const bonePnts = [midTail].concat(pnts.slice(2));

		// 计算箭头
		const headPnts = this.getArrowHeadPoints(bonePnts, tailLeft, tailRight);
		const neckLeft = headPnts[0];
		const neckRight = headPnts[4];
		const tailWidthFactor =
			calcDistance(tailLeft, tailRight) / getBaseLength(bonePnts);

		// 计算箭身
		const bodyPnts = this.getArrowBodyPoints(
			bonePnts,
			neckLeft,
			neckRight,
			tailWidthFactor,
		);

		// 整合
		const count = bodyPnts.length;
		let leftPnts = [tailLeft].concat(bodyPnts.slice(0, count / 2));
		leftPnts.push(neckLeft);
		let rightPnts = [tailRight].concat(bodyPnts.slice(count / 2, count));
		rightPnts.push(neckRight);

		leftPnts = getQBSplinePoints(leftPnts);
		rightPnts = getQBSplinePoints(rightPnts);

		const coords = leftPnts.concat(headPnts, rightPnts.reverse());

		return coords;
	}
}

class DoubleArrow {
	static headHeightFactor = 0.25;
	static headWidthFactor = 0.3;
	static neckHeightFactor = 0.85;
	static neckWidthFactor = 0.15;

	constructor() {}

	static getArrowHeadPoints(points) {
		const len = getBaseLength(points);
		const headHeight = len * this.headHeightFactor;
		const headPnt = points[points.length - 1];
		const headWidth = headHeight * this.headWidthFactor;
		const neckWidth = headHeight * this.neckWidthFactor;
		const neckHeight = headHeight * this.neckHeightFactor;

		const headEndPnt = getThirdPoint(
			points[points.length - 2],
			headPnt,
			0,
			headHeight,
			true,
		);

		const neckEndPnt = getThirdPoint(
			points[points.length - 2],
			headPnt,
			0,
			neckHeight,
			true,
		);

		const headLeft = getThirdPoint(
			headPnt,
			headEndPnt,
			HALF_PI,
			headWidth,
			false,
		);

		const headRight = getThirdPoint(
			headPnt,
			headEndPnt,
			HALF_PI,
			headWidth,
			true,
		);

		const neckLeft = getThirdPoint(
			headPnt,
			neckEndPnt,
			HALF_PI,
			neckWidth,
			false,
		);

		const neckRight = getThirdPoint(
			headPnt,
			neckEndPnt,
			HALF_PI,
			neckWidth,
			true,
		);

		return [neckLeft, headLeft, headPnt, headRight, neckRight];
	}

	static getArrowBodyPoints(points, neckLeft, neckRight, tailWidthFactor) {
		const allLen = wholeDistance(points);
		const len = getBaseLength(points);
		const tailWidth = len * tailWidthFactor;
		const neckWidth = calcDistance(neckLeft, neckRight);
		const widthDif = (tailWidth - neckWidth) / 2;
		let tempLen = 0,
			leftBodyPnts = [],
			rightBodyPnts = [];

		for (let i = 1; i < points.length - 1; i++) {
			const angle =
				getAngleOfThreePoints(points[i - 1], points[i], points[i + 1]) / 2;
			tempLen += calcDistance(points[i - 1], points[i]);
			const w =
				(tailWidth / 2 - (tempLen / allLen) * widthDif) / Math.sin(angle);
			const left = getThirdPoint(
				points[i - 1],
				points[i],
				Math.PI - angle,
				w,
				true,
			);

			const right = getThirdPoint(points[i - 1], points[i], angle, w, false);
			leftBodyPnts.push(left);
			rightBodyPnts.push(right);
		}
		return leftBodyPnts.concat(rightBodyPnts);
	}

	static getArrowPoints(pnt1, pnt2, pnt3, clockWise) {
		const midPnt = calcMid(pnt1, pnt2);
		const len = calcDistance(midPnt, pnt3);
		let midPnt1 = getThirdPoint(pnt3, midPnt, 0, len * 0.3, true);
		let midPnt2 = getThirdPoint(pnt3, midPnt, 0, len * 0.5, true);
		midPnt1 = getThirdPoint(midPnt, midPnt1, HALF_PI, len / 5, clockWise);
		midPnt2 = getThirdPoint(midPnt, midPnt2, HALF_PI, len / 4, clockWise);

		const points = [midPnt, midPnt1, midPnt2, pnt3];

		// 计算箭头部分
		const arrowPnts = this.getArrowHeadPoints(points);
		const neckLeftPoint = arrowPnts[0];
		const neckRightPoint = arrowPnts[4];

		// 计算箭身部分
		const tailWidthFactor =
			calcDistance(pnt1, pnt2) / getBaseLength(points) / 2;
		const bodyPnts = this.getArrowBodyPoints(
			points,
			neckLeftPoint,
			neckRightPoint,
			tailWidthFactor,
		);
		const n = bodyPnts.length;
		let lPoints = bodyPnts.slice(0, n / 2);
		let rPoints = bodyPnts.slice(n / 2, n);
		lPoints.push(neckLeftPoint);
		rPoints.push(neckRightPoint);
		lPoints = lPoints.reverse();
		lPoints.push(pnt2);
		rPoints = rPoints.reverse();
		rPoints.push(pnt1);

		return lPoints.reverse().concat(arrowPnts, rPoints);
	}

	static generate(pnts) {
		const pnt1 = pnts[0];
		const pnt2 = pnts[1];
		const pnt3 = pnts[2];
		const count = pnts.length;

		let tempPoint4;
		if (count == 3) {
			tempPoint4 = getTempPoint4(pnt1, pnt2, pnt3);
		} else {
			tempPoint4 = pnts[3];
		}

		const connPoint = calcMid(pnt1, pnt2);

		let leftArrowPnts, rightArrowPnts;

		if (isClockWise(pnt1, pnt2, pnt3)) {
			leftArrowPnts = this.getArrowPoints(pnt1, connPoint, tempPoint4, false);
			rightArrowPnts = this.getArrowPoints(connPoint, pnt2, pnt3, true);
		} else {
			leftArrowPnts = this.getArrowPoints(pnt2, connPoint, pnt3, false);
			rightArrowPnts = this.getArrowPoints(connPoint, pnt1, tempPoint4, true);
		}
		const m = leftArrowPnts.length;
		const t = (m - 5) / 2;

		const llBodyPnts = leftArrowPnts.slice(0, t);
		const lArrowPnts = leftArrowPnts.slice(t, t + 5);
		let lrBodyPnts = leftArrowPnts.slice(t + 5, m);

		let rlBodyPnts = rightArrowPnts.slice(0, t);
		const rArrowPnts = rightArrowPnts.slice(t, t + 5);
		const rrBodyPnts = rightArrowPnts.slice(t + 5, m);

		rlBodyPnts = getBezierPoints(rlBodyPnts);
		const bodyPnts = getBezierPoints(rrBodyPnts.concat(llBodyPnts.slice(1)));
		lrBodyPnts = getBezierPoints(lrBodyPnts);

		const coords = rlBodyPnts.concat(
			rArrowPnts,
			bodyPnts,
			lArrowPnts,
			lrBodyPnts,
		);

		return coords;
	}
}

class FineArrow {
	static maxArrowLength = 3000000;
	static arrowLengthScale = 5;

	static generate(pnts) {
		const pnt1 = pnts[0];
		const pnt2 = pnts[1];
		const distance = calcDistance(pnt1, pnt2);
		let len = distance / this.arrowLengthScale;
		len = len > this.maxArrowLength ? this.maxArrowLength : len;
		const leftPnt = getThirdPoint(pnt1, pnt2, Math.PI / 6, len, false);
		const rightPnt = getThirdPoint(pnt1, pnt2, Math.PI / 6, len, true);
		const coords = [pnt1, pnt2, leftPnt, pnt2, rightPnt];

		return coords;
	}
}

class GatheringPlace {
	static generate(pnts) {
		let newPnts = [...pnts];

		if (pnts.length === 2) {
			const mid1 = calcMid(pnts[0], pnts[1]);
			const distance = calcDistance(pnts[0], mid1) / 0.9;
			const pnt = getThirdPoint(pnts[0], mid1, HALF_PI, distance, true);

			newPnts = [pnts[0], pnt, pnts[1]];
		}

		const mid2 = calcMid(newPnts[0], newPnts[2]);
		newPnts.push(mid2, newPnts[0], newPnts[1]);

		let normals = [];
		for (let i = 0; i < newPnts.length - 2; i++) {
			const pnt1 = newPnts[i];
			const pnt2 = newPnts[i + 1];
			const pnt3 = newPnts[i + 2];
			const normalPoints = getBisectorNormals(0.4, pnt1, pnt2, pnt3);
			normals.push(...normalPoints);
		}

		const count = normals.length;
		normals = [normals[count - 1]].concat(normals.slice(0, count - 1));

		const coords = [];
		for (let i = 0; i < newPnts.length - 2; i++) {
			const pnt1 = newPnts[i];
			const pnt2 = newPnts[i + 1];

			for (let t = 0; t < FITTING_COUNT; t++) {
				const pnt = getCubicValue(
					t / FITTING_COUNT,
					pnt1,
					normals[i * 2],
					normals[i * 2 + 1],
					pnt2,
				);
				coords.push(pnt);
			}
			coords.push(pnt2);
		}

		// console.log("gathering place coords:", coords);

		return coords;
	}
}

class StraightArrow {
	static tailWidthFactor = 0.15;
	static tailWidthFactor = 0.15;
	static neckWidthFactor = 0.2;
	static headWidthFactor = 0.25;
	static headAngle = Math.PI / 8.5;
	static neckAngle = Math.PI / 13;

	static generate(pnts) {
		const pnt1 = pnts[0];
		const pnt2 = pnts[1];
		const len = getBaseLength(pnts);
		const tailWidth = len * this.tailWidthFactor;
		const neckWidth = len * this.neckWidthFactor;
		const headWidth = len * this.headWidthFactor;
		const tailLeft = getThirdPoint(pnt2, pnt1, HALF_PI, tailWidth, true);
		const tailRight = getThirdPoint(pnt2, pnt1, HALF_PI, tailWidth, false);
		const headLeft = getThirdPoint(
			pnt1,
			pnt2,
			this.headAngle,
			headWidth,
			false,
		);
		const headRight = getThirdPoint(
			pnt1,
			pnt2,
			this.headAngle,
			headWidth,
			true,
		);
		const neckLeft = getThirdPoint(
			pnt1,
			pnt2,
			this.neckAngle,
			neckWidth,
			false,
		);
		const neckRight = getThirdPoint(
			pnt1,
			pnt2,
			this.neckAngle,
			neckWidth,
			true,
		);
		const coords = [
			tailLeft,
			neckLeft,
			headLeft,
			pnt2,
			headRight,
			neckRight,
			tailRight,
		];

		return coords;
	}
}

class ThreeArrow {
	static headHeightFactor = 0.25;
	static headWidthFactor = 0.3;
	static neckHeightFactor = 0.85;
	static neckWidthFactor = 0.15;

	constructor() {}

	static generate(pnts) {
		const pnt1 = pnts[0];
		const pnt2 = pnts[1];
		const pnt3 = pnts[2];
		const count = pnts.length; // 获取已经点击的坐标数

		let tempPoint4;
		let tempPoint5;

		// 计算第 4、5 个点
		if (count == 3) {
			// 鼠标绘制了三个点
			// 第四个点为第三个点的对称点
			// 第五个点是第三个点和第四个点的中点
			tempPoint4 = getTempPoint4(pnt1, pnt2, pnt3);
			tempPoint5 = calcMid(pnt3, tempPoint4);
		} else if (count == 4) {
			// 鼠标绘制了四个点
			// 第五个点是第三个点和第四个点的中点
			tempPoint4 = pnts[3];
			tempPoint5 = calcMid(pnt3, tempPoint4);
		} else {
			tempPoint4 = pnts[3];
			tempPoint5 = pnts[4];
		}

		const connPoint = calcMid(pnt1, pnt2);
		let leftArrowPnts, rightArrowPnts;

		// 判断是否为顺时针绘制
		if (isClockWise(pnt1, pnt2, pnt3)) {
			leftArrowPnts = DoubleArrow.getArrowPoints(
				pnt1,
				connPoint,
				tempPoint4,
				false,
			);
			rightArrowPnts = DoubleArrow.getArrowPoints(connPoint, pnt2, pnt3, true);
		} else {
			leftArrowPnts = DoubleArrow.getArrowPoints(pnt2, connPoint, pnt3, false);
			rightArrowPnts = DoubleArrow.getArrowPoints(
				connPoint,
				pnt1,
				tempPoint4,
				true,
			);
		}

		const m = leftArrowPnts.length;
		const t = (m - 5) / 2;

		const llBodyPnts = leftArrowPnts.slice(0, t);
		const lArrowPnts = leftArrowPnts.slice(t, t + 5);
		let lrBodyPnts = leftArrowPnts.slice(t + 5, m);

		let rlBodyPnts = rightArrowPnts.slice(0, t);
		const rArrowPnts = rightArrowPnts.slice(t, t + 5);
		const rrBodyPnts = rightArrowPnts.slice(t + 5, m);

		rlBodyPnts = getBezierPoints(rlBodyPnts);
		const centerPnts = rrBodyPnts.concat(llBodyPnts.slice(1));
		lrBodyPnts = getBezierPoints(lrBodyPnts);

		const centerArrowPnts = AttackArrow.generate([
			centerPnts[2],
			centerPnts[4],
			tempPoint5,
		]);

		const canterLen = centerPnts.length;
		const centerArowLen = centerArrowPnts.length;
		const clMid = calcMid(centerArrowPnts[0], centerArrowPnts[1]);
		const clPnts = getBezierPoints([
			centerPnts[0],
			centerPnts[1],
			centerPnts[2],
			centerArrowPnts[0],
			clMid,
		]);

		const crMid = calcMid(
			centerArrowPnts[centerArowLen - 2],
			centerArrowPnts[centerArowLen - 1],
		);
		const crPnts = getBezierPoints([
			crMid,
			centerArrowPnts[centerArowLen - 1],
			centerPnts[canterLen - 3],
			centerPnts[canterLen - 2],
			centerPnts[canterLen - 1],
		]);

		const center = centerArrowPnts.slice(2, centerArowLen - 2);
		const newCenterArrowPnts = [...clPnts, ...center, ...crPnts];

		const coords = rlBodyPnts.concat(
			rArrowPnts,
			newCenterArrowPnts,
			lArrowPnts,
			lrBodyPnts,
		);

		return coords;
	}
}

const GenerateDict = {
	// draw_rectangle: DrawRectangle,
	// draw_icon: DrawIcon,
	// draw_text: DrawText,
	// draw_circle: DrawCircle,
	// draw_ellipse: DrawEllipse,
	draw_attack_arrow: AttackArrow.generate.bind(AttackArrow),
	draw_double_arrow: DoubleArrow.generate.bind(DoubleArrow),
	draw_three_arrow: ThreeArrow.generate.bind(ThreeArrow),
	draw_straight_arrow: StraightArrow.generate.bind(StraightArrow),
	draw_fine_arrow: FineArrow.generate.bind(FineArrow),
	draw_gathering_place: GatheringPlace.generate.bind(GatheringPlace),
};

const toLineGeojson = (coords, properties) => {
	const geojson = turf.lineString(coords, properties);

	return geojson;
};

const doubleClickZoom$1 = MapboxDraw.lib.doubleClickZoom;

function isEventAtCoordinates(coordinates, lngLat) {
	if (!lngLat) return false;

	const { lng, lat } = lngLat;
	for (let i = 0; i < coordinates.length; i++) {
		const coord = coordinates[i];
		if (coord[0] === lng && coord[1] === lat) {
			return true;
		}
	}
	return false;
}

const DrawAttackArrow = {
	onSetup: function (opts) {
		const clickCount = 0;

		doubleClickZoom$1.disable(this);

		return {
			coordinates: [],
			clickCount,
		};
	},
	onClick: function (state, e) {
		const { lngLat } = e;
		const { coordinates, clickCount } = state;

		if (
			isEventAtCoordinates(coordinates, lngLat) &&
			clickCount === coordinates.length
		) {
			this.map.fire("draw.create", {
				mode: "attack_arrow",
				id: state.feature.id,
				features: [state.feature.toGeoJSON()],
				controlPnts: state.coordinates,
			});

			this.changeMode("simple_select", {}, { silent: true });
			return;
		}

		coordinates[clickCount] = [lngLat.lng, lngLat.lat];
		state.clickCount += 1;
	},

	onMouseMove: function (state, e) {
		// console.log("onmousemove--state", state);
		const { clickCount, coordinates } = state;

		if (clickCount > 1) {
			coordinates[clickCount] = [e.lngLat.lng, e.lngLat.lat];
		}

		this.updateFeature(state);
	},

	onStop: function (state) {
		doubleClickZoom$1.enable(this);

		state.coordinates = [];
		state.clickCount = 0;
	},

	toDisplayFeatures: (state, geojson, display) => {
		display(geojson);
	},

	updateFeature: function (state) {
		const { coordinates, feature } = state;
		if (coordinates.length < 3) return;

		const coords = AttackArrow.generate(coordinates);
		const arrowFeat = toLineGeojson(coords);

		if (feature) {
			feature.setCoordinates(arrowFeat.geometry.coordinates);
		} else {
			const feat = this.newFeature(arrowFeat);
			this.addFeature(feat);

			state.feature = feat;
		}
	},
};

function calculateDistance(start, end) {
	const p1 = turf.point(start);
	const p2 = turf.point(end);
	const options = { units: "kilometers" };

	return turf.distance(p1, p2, options);
}

function createCircleFeature(startPos, endPos) {
	const radius = calculateDistance(startPos, endPos);
	const options = {
		properties: {
			isCircle: true,
			center: startPos,
			radiusInKm: radius,
		},
	};

	return turf.circle(startPos, radius, options);
}

const DrawCircle = {
	onSetup: (opts) => {
		const state = { clickState: false };
		return state;
	},
	onClick: function (state, e) {
		state.clickState = !state.clickState;

		if (state.clickState) {
			state.coordinates = [e.lngLat.lng, e.lngLat.lat];
		} else {
			this.deleteFeature("draw_circle_temp");
			const circle = this.newFeature(
				createCircleFeature(state.coordinates, [e.lngLat.lng, e.lngLat.lat]),
			);
			this.addFeature(circle);

			const coordinates = JSON.parse(JSON.stringify(circle.coordinates));

			const feature = JSON.parse(
				JSON.stringify({
					id: circle.id,
					type: "Feature",
					geometry: {
						type: "Polygon",
						properties: {},
						coordinates,
					},
				}),
			);

			this.map.fire("draw.create", {
				features: [feature],
				mode: "draw_circle",
			});
			return this.changeMode("simple_select");
		}
	},
	onMouseMove: function (state, e) {
		if (state.clickState) {
			const temp = this.getFeature("draw_circle_temp");
			if (!temp) {
				this.addFeature(
					this.newFeature(
						Object.assign(
							createCircleFeature(state.coordinates, [
								e.lngLat.lng,
								e.lngLat.lat,
							]),
							{
								id: "draw_circle_temp",
							},
						),
					),
				);
			} else {
				const newFeat = createCircleFeature(state.coordinates, [
					e.lngLat.lng,
					e.lngLat.lat,
				]);
				temp.setProperty("radiusInKm", newFeat.properties.radiusInKm);
				temp.setCoordinates(newFeat.geometry.coordinates);
			}
		}
	},

	toDisplayFeatures: (state, geojson, display) => {
		display(geojson);
	},
};

const DrawDoubleArrow = {
	onSetup: () => {
		const clickCount = 0;

		return {
			coordinates: [],
			clickCount,
		};
	},
	onClick: function (state, e) {
		const { coordinates } = state;

		coordinates[state.clickCount] = [e.lngLat.lng, e.lngLat.lat];
		state.clickCount += 1;

		if (state.clickCount === 4) {
			this.map.fire("draw.create", {
				features: [state.feature.toGeoJSON()],
				mode: "double_arrow",
				controlPnts: state.coordinates,
			});
			this.changeMode("simple_select", {}, { silent: true });
		}
	},
	onMouseMove: function (state, e) {
		// console.log("onmousemove--state", state);
		const { clickCount, coordinates } = state;

		if (clickCount > 1) {
			coordinates[clickCount] = [e.lngLat.lng, e.lngLat.lat];
		}

		this.updateFeature(state);
	},

	toDisplayFeatures: (state, geojson, display) => {
		display(geojson);
	},

	updateFeature: function (state) {
		const { coordinates, feature } = state;
		if (coordinates.length < 3) return;

		const coords = DoubleArrow.generate(coordinates);
		const arrowFeat = toLineGeojson(coords);

		if (feature) {
			feature.setCoordinates(arrowFeat.geometry.coordinates);
		} else {
			const feat = this.newFeature(arrowFeat);
			this.addFeature(feat);

			state.feature = feat;
		}
	},
};

const doubleClickZoom = {
	enable: (ctx) => {
		setTimeout(() => {
			if (
				!ctx.map ||
				!ctx.map.doubleClickZoom ||
				!ctx._ctx ||
				!ctx._ctx.store ||
				!ctx._ctx.store.getInitialConfigValue
			) {
				return;
			}

			if (!ctx._ctx.store.getInitialConfigValue("doubleClickZoom")) return;

			ctx.map.doubleClickZoom.enable();
		}, 0);
	},
	disable: (ctx) => {
		setTimeout(() => {
			if (!ctx.map || !ctx.map.doubleClickZoom) return;

			ctx.map.doubleClickZoom.disable();
		}, 0);
	},
};

const DrawEllipse = {
	onSetup: function (opts) {
		this.eccentricity =
			opts.eccentricity >= 0 && opts.eccentricity < 1 ? opts.eccentricity : 0.8;
		this.divisions = opts.divisions || 60;

		const ellipse = this.newFeature({
			type: "Feature",
			properties: {},
			geometry: {
				type: "Polygon",
				coordinates: [[]],
			},
		});

		this.addFeature(ellipse);
		this.clearSelectedFeatures();
		doubleClickZoom.disable(this);
		this.updateUIClasses({ mouse: "add" });
		this.setActionableState({
			trash: true,
		});

		return {
			ellipse,
		};
	},

	onClick: function (state, e) {
		if (
			state.center &&
			state.center[0] !== e.lngLat.lng &&
			state.center[1] !== e.lngLat.lat
		) {
			this.updateUIClasses({ mouse: "pointer" });
			this.changeMode("simple_select", { featuresId: state.ellipse.id });
		}

		state.center = [e.lngLat.lng, e.lngLat.lat];
	},

	onMouseMove: function (state, e) {
		if (state.center) {
			const xRadius = Math.sqrt(
				(e.lngLat.lng - state.center[0]) ** 2 +
					(e.lngLat.lat - state.center[1]) ** 2,
			);
			const yRadius = xRadius * Math.sqrt(1 - this.eccentricity ** 2);

			const radian = Math.atan2(
				e.lngLat.lat - state.center[1],
				e.lngLat.lng - state.center[0],
			);

			const twoPi = Math.PI * 2;

			for (let i = 0; i < this.divisions; i++) {
				const t = i / this.divisions;
				const angle = t * twoPi;

				let x = state.center[0] + xRadius * Math.cos(angle);
				let y = state.center[1] + yRadius * Math.sin(angle);

				if (radian !== 0) {
					const cos = Math.cos(radian);
					const sin = Math.sin(radian);

					const tx = x - state.center[0];
					const ty = y - state.center[1];

					// 围绕椭圆中心旋转点。
					x = tx * cos - ty * sin + state.center[0];
					y = tx * sin + ty * cos + state.center[1];
				}

				// 更新要素坐标
				state.ellipse.updateCoordinate("0." + i, x, y);
			}
		}
	},
	onKeyUp: function (state, e) {
		if (e.keyCode === 27) return this.changeMode("simple_select");
	},
	onStop: function (state) {
		doubleClickZoom.enable(this);
		this.updateUIClasses({ mouse: "none" });
		this.activateUIButton();

		// 检查我们是否删除了此要素
		if (this.getFeature(state.ellipse.id) === undefined) return;

		if (state.ellipse.isValid()) {
			this.map.fire("draw.create", {
				features: [state.ellipse.toGeoJSON()],
				mode: "draw_ellipse",
			});
		} else {
			this.deleteFeature([state.ellipse.id], { silent: true });
			this.changeMode("simple_select", {}, { silent: true });
		}
	},

	toDisplayFeatures: (state, geojson, display) => {
		const isActivePolygon = geojson.properties.id === state.ellipse.id;
		geojson.properties.active = isActivePolygon ? "true" : "false";
		if (!isActivePolygon) return display(geojson);

		if (!state.center) return;
		return display(geojson);
	},
	onTrash: function (state) {
		this.deleteFeature([state.ellipse.id], { silent: true });
		this.changeMode("simple_select");
	},
};

const DrawFineArrow = {
	onSetup: () => {
		const clickCount = 0;

		return {
			coordinates: [],
			clickCount,
		};
	},
	onClick: function (state, e) {
		const { coordinates } = state;

		coordinates[state.clickCount] = [e.lngLat.lng, e.lngLat.lat];
		state.clickCount += 1;

		if (state.clickCount === 2) {
			this.map.fire("draw.create", {
				features: [state.feature.toGeoJSON()],
				mode: "draw_fine_arrow",
				controlPnts: state.coordinates,
			});
			this.changeMode("simple_select", {}, { silent: true });
		}
	},
	onMouseMove: function (state, e) {
		// console.log("onmousemove--state", state);
		const { clickCount, coordinates } = state;

		if (clickCount > 0) {
			coordinates[clickCount] = [e.lngLat.lng, e.lngLat.lat];
		}

		this.updateFeature(state);
	},

	toDisplayFeatures: (state, geojson, display) => {
		display(geojson);
	},

	updateFeature: function (state) {
		const { coordinates, feature } = state;
		if (coordinates.length < 2) return;

		const coords = FineArrow.generate(coordinates);
		const arrowFeat = toLineGeojson(coords);

		if (feature) {
			feature.setCoordinates(arrowFeat.geometry.coordinates);
		} else {
			const feat = this.newFeature(arrowFeat);
			this.addFeature(feat);

			state.feature = feat;
		}
	},
};

const DrawGatheringPlace = {
	onSetup: () => {
		const clickCount = 0;

		return {
			coordinates: [],
			clickCount,
		};
	},
	onClick: function (state, e) {
		const { coordinates } = state;

		coordinates[state.clickCount] = [e.lngLat.lng, e.lngLat.lat];
		state.clickCount += 1;

		if (state.clickCount === 3) {
			this.map.fire("draw.create", {
				features: [state.feature.toGeoJSON()],
				mode: "draw_gathering_place",
				controlPnts: state.coordinates,
			});
			this.changeMode("simple_select", {}, { silent: true });
		}
	},
	onMouseMove: function (state, e) {
		// console.log("onmousemove--state", state);
		const { clickCount, coordinates } = state;

		if (clickCount > 0) {
			coordinates[clickCount] = [e.lngLat.lng, e.lngLat.lat];
		}

		this.updateFeature(state);
	},

	toDisplayFeatures: (state, geojson, display) => {
		display(geojson);
	},

	updateFeature: function (state) {
		const { coordinates, feature } = state;
		if (coordinates.length < 2) return;

		const coords = GatheringPlace.generate(coordinates);
		const arrowFeat = toLineGeojson(coords);

		if (feature) {
			feature.setCoordinates(arrowFeat.geometry.coordinates);
		} else {
			const feat = this.newFeature(arrowFeat);
			this.addFeature(feat);

			state.feature = feat;
		}
	},
};

const DrawIcon = {
	onSetup: (opts) => {
		const state = {};

		state.icon = opts.icon || "draw-icon";
		state.iconSize = opts.iconSize;

		return state;
	},

	onClick: function (state, e) {
		const point = this.newFeature({
			type: "Feature",
			properties: {
				icon: state.icon,
				iconSize: state.iconSize,
			},
			geometry: {
				type: "Point",
				coordinates: [e.lngLat.lng, e.lngLat.lat],
			},
		});

		this.addFeature(point);

		this.map.fire("draw.create", {
			features: [point.toGeoJSON()],
			mode: "draw_icon",
		});

		this.changeMode("simple_select");
	},

	toDisplayFeatures: (state, geojson, display) => {
		display(geojson);
	},
};

const DrawStraightArrow = {
	onSetup: () => {
		const clickCount = 0;

		return {
			coordinates: [],
			clickCount,
		};
	},
	onClick: function (state, e) {
		const { coordinates } = state;

		coordinates[state.clickCount] = [e.lngLat.lng, e.lngLat.lat];
		state.clickCount += 1;

		if (state.clickCount === 2) {
			this.map.fire("draw.create", {
				features: [state.feature.toGeoJSON()],
				mode: "draw_straight_arrow",
				controlPnts: state.coordinates,
			});
			this.changeMode("simple_select", {}, { silent: true });
		}
	},
	onMouseMove: function (state, e) {
		// console.log("onmousemove--state", state);
		const { clickCount, coordinates } = state;

		if (clickCount > 0) {
			coordinates[clickCount] = [e.lngLat.lng, e.lngLat.lat];
		}

		this.updateFeature(state);
	},

	toDisplayFeatures: (state, geojson, display) => {
		display(geojson);
	},

	updateFeature: function (state) {
		const { coordinates, feature } = state;
		if (coordinates.length < 2) return;

		const coords = StraightArrow.generate(coordinates);
		const arrowFeat = toLineGeojson(coords);

		if (feature) {
			feature.setCoordinates(arrowFeat.geometry.coordinates);
		} else {
			const feat = this.newFeature(arrowFeat);
			this.addFeature(feat);

			state.feature = feat;
		}
	},
};

const DrawText = {
	onSetup: (opts) => {
		const state = {};
		const { onDraw, onCancel } = opts;

		state.onDraw = onDraw;
		state.onCancel = onCancel;

		return state;
	},

	onClick: function (state, e) {
		const coordinates = [e.lngLat.lng, e.lngLat.lat];
		const point = this.newFeature({
			type: "Feature",
			properties: {
				label: "文字标注",
				text: "文字标注",
			},
			geometry: {
				type: "Point",
				coordinates,
			},
		});
		this.addFeature(point);

		state.point = point;
		this.changeMode("simple_select");
	},
	toDisplayFeatures: (state, geojson, display) => {
		display(geojson);
	},
	onStop: function (state) {
		const f = state.point;

		this.updateUIClasses({ mouse: MapboxDraw.constants.cursors.NONE });
		MapboxDraw.lib.doubleClickZoom.enable(this);
		this.activateUIButton();

		const drawnFeature = this.getFeature(f.id);
		if (drawnFeature === undefined) {
			if (typeof state.onCancel === "function") state.onCancel();
			return;
		}

		if (f.isValid()) {
			if (typeof state.onDraw === "function") state.onDraw(f.toGeoJSON());
			else {
				// this.map.fire("draw.text", {
				//   features: [f.toGeoJSON()],
				//   mode: "draw_text",
				// });

				this.map.fire("draw.create", {
					features: [f.toGeoJSON()],
					mode: "draw_text",
				});
			}
		}

		this.deleteFeature([f.id], { silent: true });
		this.changeMode("simple_select", {}, { silent: true });
	},
};

const DrawThreeArrow = {
	onSetup: () => {
		const clickCount = 0;

		return {
			coordinates: [],
			clickCount,
		};
	},
	onClick: function (state, e) {
		const { coordinates } = state;

		coordinates[state.clickCount] = [e.lngLat.lng, e.lngLat.lat];
		state.clickCount += 1;

		if (state.clickCount === 5) {
			this.map.fire("draw.create", {
				features: [state.feature.toGeoJSON()],
				mode: "three_arrow",
				controlPnts: state.coordinates,
			});
			this.changeMode("simple_select", {}, { silent: true });
		}
	},
	onMouseMove: function (state, e) {
		// console.log("onmousemove--state", state);
		const { clickCount, coordinates } = state;

		if (clickCount > 1) {
			coordinates[clickCount] = [e.lngLat.lng, e.lngLat.lat];
		}

		this.updateFeature(state);
	},

	toDisplayFeatures: (state, geojson, display) => {
		display(geojson);
	},

	updateFeature: function (state) {
		const { coordinates, feature } = state;
		if (coordinates.length < 3) return;

		const coords = ThreeArrow.generate(coordinates);
		const arrowFeat = toLineGeojson(coords);

		if (feature) {
			feature.setCoordinates(arrowFeat.geometry.coordinates);
		} else {
			const feat = this.newFeature(arrowFeat);
			this.addFeature(feat);

			state.feature = feat;
		}
	},
};

const MapboxDrawExtends = {
	// draw_rectangle: DrawRectangle,
	draw_icon: DrawIcon,
	draw_text: DrawText,
	draw_circle: DrawCircle,
	draw_ellipse: DrawEllipse,
	draw_attack_arrow: DrawAttackArrow,
	draw_double_arrow: DrawDoubleArrow,
	draw_three_arrow: DrawThreeArrow,
	draw_gathering_place: DrawGatheringPlace,
	draw_fine_arrow: DrawFineArrow,
	draw_straight_arrow: DrawStraightArrow,
};

// [参考](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/)

let EditControl$2 = class EditControl {
	constructor(map) {
		this.map = map;
		this.canvas = map.getCanvasContainer();

		this.points = [];
		this.editSource = null;

		this.enterFunc = null;
		this.leaveFunc = null;
		this.downFunc = null;
		this.moveFunc = null;
		this.upFunc = null;
	}

	bindEvents(callback) {
		const { map, editLayerId } = this;

		this.enterFunc = this.onMouseEnter.bind(this);
		this.leaveFunc = this.onMouseLeave.bind(this);
		this.downFunc = this.onMouseDown.bind(this);
		this.moveFunc = this.onMove.bind(this, callback);
		this.upFunc = this.onUp.bind(this);

		map.on("mouseenter", editLayerId, this.enterFunc);
		map.on("mouseleave", editLayerId, this.leaveFunc);
		map.on("mousedown", editLayerId, this.downFunc);
	}

	onMouseEnter() {
		const { canvas } = this;
		canvas.style.cursor = "move";
	}

	onMouseLeave() {
		const { canvas } = this;
		canvas.style.cursor = "";
	}

	onMouseDown(e) {
		// Prevent the default map drag behavior.
		e.preventDefault();

		const { canvas, map } = this;

		canvas.style.cursor = "grab";

		map.on("mousemove", this.moveFunc);
		map.once("mouseup", this.upFunc);
	}

	onUp() {
		const { canvas, map } = this;

		canvas.style.cursor = "";

		map.off("mousemove", this.moveFunc);
	}

	destroy() {
		const { map, editLayerId } = this;

		map.off("mouseenter", this.enterFunc);
		map.off("mouseleave", this.leaveFunc);
		map.off("mousedown", this.downFunc);

		map.removeLayer(editLayerId);
		map.removeSource(editLayerId);
	}
};

function toPointGeojson(points, properties) {
	const features = points.map((point) => turf.point(point, properties));
	const data = turf.featureCollection(features);
	return data;
}

function toEnvelopedGeojson(points, properties) {
	const features = points.map((point) => turf.point(point, properties));
	const collection = turf.featureCollection(features);
	const enveloped = turf.envelope(collection);

	return enveloped;
}

function toCentroidGeojson(points) {
	const features = points.map((point) => turf.point(point));
	const collection = turf.featureCollection(features);
	const center = turf.centroid(collection);

	return center;
}

function getEditIndex(points, latLng, radius) {
	// const editIndex = points.findIndex((point) => {
	//   const source = turf.point(point);
	//   const buffered = turf.buffer(source, radius, { units: "degrees" });
	//   const target = turf.point([latLng.lng, latLng.lat]);

	//   const isWithin = turf.booleanPointInPolygon(target, buffered, {
	//     ignoreBoundary: false,
	//   });

	//   return isWithin;
	// });

	const editIndex = points.findIndex(
		(point) =>
			Math.abs(point[0] - latLng.lng) < radius &&
			Math.abs(point[1] === latLng.lat) < radius,
	);

	return editIndex;
}

let EditControl$1 = class EditControl extends EditControl$2 {
	constructor(map) {
		super(map);

		this.editLayerId = "edit-control-layer";

		this.editIndex = -1;

		this.initEditLayer();
	}

	initEditLayer() {
		const { map, editLayerId } = this;

		const editLControLayer = {
			id: editLayerId,
			type: "circle",
			source: {
				type: "geojson",
				data: {
					type: "FeatureCollection",
					features: [],
				},
			},
			paint: {
				"circle-color": "#F84C4C", // red
				"circle-radius": 5,
				"circle-opacity": 1,
			},
		};

		map.addLayer(editLControLayer);

		this.editSource = map.getSource(editLayerId);
	}

	bindEvents(callback) {
		const { map, editLayerId } = this;

		this.enterFunc = this.onMouseEnter.bind(this);
		this.leaveFunc = this.onMouseLeave.bind(this);
		this.downFunc = this.onMouseDown.bind(this);
		this.moveFunc = this.onMove.bind(this, callback);
		this.upFunc = this.onUp.bind(this);

		map.on("mouseenter", editLayerId, this.enterFunc);
		map.on("mouseleave", editLayerId, this.leaveFunc);
		map.on("mousedown", editLayerId, this.downFunc);
	}

	onMouseEnter() {
		const { map, canvas, editLayerId } = this;
		map.setPaintProperty(editLayerId, "circle-color", "#3bb2d0");
		canvas.style.cursor = "move";
	}

	onMouseLeave() {
		const { map, canvas, editLayerId } = this;
		map.setPaintProperty(editLayerId, "circle-color", "#3887be");
		canvas.style.cursor = "";
	}

	onMouseDown(e) {
		const { map, canvas } = this;
		// Prevent the default map drag behavior.
		e.preventDefault();

		canvas.style.cursor = "grab";

		const zoom = map.getZoom();
		const radius = 0.1 * (1 / zoom); // 根据zoom设置半径
		this.editIndex = getEditIndex(this.points, e.lngLat, radius);

		// console.log("editIndex", this.editIndex);

		map.on("mousemove", this.moveFunc);
		map.once("mouseup", this.upFunc);
	}

	onUp() {
		const { canvas, map } = this;

		canvas.style.cursor = "";
		map.off("mousemove", this.moveFunc);
	}

	onMove(callback, e) {
		const { canvas, editIndex, points, editSource } = this;

		const coords = e.lngLat;
		canvas.style.cursor = "grabbing";

		points[editIndex] = [coords.lng, coords.lat];

		const data = toPointGeojson(points);
		editSource.setData(data);

		callback(points);
	}

	updateEditSource(points) {
		const data = toPointGeojson(points);
		this.editSource.setData(data);

		this.points = points;
	}

	destroy() {
		const { map, editLayerId } = this;

		map.off("mouseenter", this.enterFunc);
		map.off("mouseleave", this.leaveFunc);
		map.off("mousedown", this.downFunc);

		map.removeLayer(editLayerId);
		map.removeSource(editLayerId);
	}
};

class EditControl extends EditControl$2 {
	constructor(map) {
		super(map);

		this.editLayerId = "edit-pan-layer";

		this.startPoint = null;

		this.initEditLayer();
	}

	initEditLayer() {
		const { map, editLayerId } = this;

		const editLPanLayer = {
			id: editLayerId,
			type: "fill",
			source: {
				type: "geojson",
				data: {
					type: "FeatureCollection",
					features: [],
				},
			},
			paint: {
				"fill-color": "yellow",
				"fill-opacity": 0.1,
				"fill-outline-color": "red",
			},
		};

		map.addLayer(editLPanLayer);

		this.editSource = map.getSource(editLayerId);
	}

	onMouseDown(e) {
		e.preventDefault();

		const { canvas, map } = this;

		canvas.style.cursor = "grab";

		this.startPoint = e.lngLat;

		map.on("mousemove", this.moveFunc);
		map.once("mouseup", this.upFunc);
	}

	onUp() {
		const { canvas, map } = this;

		this.startPoint = null;

		canvas.style.cursor = "";
		map.off("mousemove", this.moveFunc);
	}

	onMove(callback, e) {
		const { canvas, startPoint, points, editSource } = this;

		canvas.style.cursor = "grabbing";

		const coords = e.lngLat;

		const distLng = coords.lng - startPoint.lng;
		const distLat = coords.lat - startPoint.lat;

		this.startPoint = coords;

		const newPoints = points.map((point) => [
			point[0] + distLng,
			point[1] + distLat,
		]);

		// 计算平移后的点
		this.points = newPoints;

		const data = toEnvelopedGeojson(newPoints);
		editSource.setData(data);

		callback(newPoints);
	}

	updateEditSource(points) {
		const data = toEnvelopedGeojson(points);
		this.editSource.setData(data);

		this.points = points;
	}
}

// [参考](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/)

class EditRotate extends EditControl$2 {
	constructor(map) {
		super(map);

		this.editLayerId = "edit-rotate-layer";

		this.initEditLayers();
	}

	initEditLayers() {
		const { map, editLayerId } = this;

		const editRotateLayer = {
			id: editLayerId,
			type: "symbol",
			source: {
				type: "geojson",
				data: {
					type: "FeatureCollection",
					features: [],
				},
			},
			layout: {
				"icon-image": ["get", "icon"],
				"icon-size": 1,
				// "icon-offset": [0, -20],
			},
			paint: {
				"icon-opacity": 1,
				"icon-halo-color": "red",
				"icon-halo-width": 10,
			},
		};

		map.addLayer(editRotateLayer);

		this.editSource = map.getSource(editLayerId);
	}

	onMouseEnter() {
		const { canvas } = this;
		canvas.style.cursor = "move";
	}

	onMouseLeave() {
		const { canvas } = this;
		canvas.style.cursor = "";
	}

	onMove(callback, e) {
		const { canvas, points, editSource } = this;

		canvas.style.cursor = "grabbing";

		const coords = e.lngLat;
		const end = turf.point([coords.lng, coords.lat]);
		const centroid = toCentroidGeojson(points);
		const bearing1 = turf.bearing(centroid, end, { final: true });
		const bearing2 = turf.bearing(centroid, turf.point(points[0]), {
			final: true,
		});
		const angle = bearing1 - bearing2;

		const features = toPointGeojson(points);
		const rotates = turf.transformRotate(features, angle, { mutate: true });

		const newPoints = rotates.features.map(
			(feature) => feature.geometry.coordinates,
		);

		const rotatePnt = turf.point(newPoints[0], { icon: "rotate-icon" });

		editSource.setData(rotatePnt);

		callback(newPoints);
	}

	updateEditSource(points) {
		let data = null;
		if (points.length) {
			data = turf.point(points[0], { icon: "rotate-icon" });
		} else {
			data = toPointGeojson(points);
		}

		this.editSource.setData(data);

		this.points = points;
	}
}

// [参考](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/)

class EditScale extends EditControl$2 {
	constructor(map) {
		super(map);

		this.editLayerId = "edit-scale-layer";

		this.centroid = null;
		this.editIndex = -1;

		this.initEditLayers();
	}

	initEditLayers() {
		const { map, editLayerId } = this;

		const editScaleLayer = {
			id: editLayerId,
			type: "symbol",
			source: {
				type: "geojson",
				data: {
					type: "FeatureCollection",
					features: [],
				},
			},
			layout: {
				"icon-image": ["get", "icon"],
				"icon-size": 1,
				// "icon-offset": [0, -20],
			},
			paint: {
				"icon-opacity": 1,
				"icon-halo-color": "red",
				"icon-halo-width": 10,
			},
		};

		map.addLayer(editScaleLayer);

		this.editSource = map.getSource(editLayerId);
	}

	onMouseDown(e) {
		// Prevent the default map drag behavior.
		e.preventDefault();

		const { canvas, map } = this;

		canvas.style.cursor = "grab";

		const zoom = map.getZoom();
		const radius = 10 / zoom; // 根据zoom设置半径
		this.editIndex = getEditIndex(this.points, e.lngLat, radius);

		if (this.editIndex > -1) {
			this.centroid = toCentroidGeojson(this.points);

			map.on("mousemove", this.moveFunc);
			map.once("mouseup", this.upFunc);
		}
	}

	onMove(callback, e) {
		const { canvas, points, editSource, centroid, editIndex } = this;

		if (editIndex < 0) return;

		canvas.style.cursor = "grabbing";

		const start = turf.point(points[editIndex]);
		const dist1 = turf.distance(centroid, start);

		const coords = e.lngLat;
		const end = turf.point([coords.lng, coords.lat]);
		const dist2 = turf.distance(centroid, end);

		const factor = dist2 / dist1;

		const newPoints = points.map((point) => {
			const source = turf.point(point);
			const dist = turf.distance(centroid, source) * factor;
			const bearing = turf.bearing(centroid, source);
			const target = turf.destination(centroid, dist, bearing);

			return target.geometry.coordinates;
		});

		const data = toPointGeojson(newPoints, { icon: "scale-icon" });
		editSource.setData(data);

		callback(newPoints);
	}

	updateEditSource(points) {
		const data = toPointGeojson(points, { icon: "scale-icon" });
		this.editSource.setData(data);

		this.points = points;
	}
}

class MapboxEdit {
	constructor(map, options) {
		this.map = map;
		this.updateFeatureLayerFunc = options.updateFeatureLayerFunc;

		this.mode = null;
		this.edit = null;

		this.changeMode("edit_control");
	}
	changeMode(mode) {
		if (this.mode === mode) return;

		this.mode = mode;
		if (this.edit) this.edit.destroy();

		switch (mode) {
			case "edit_control":
				this.edit = new EditControl$1(this.map);
				break;

			case "edit_pan":
				this.edit = new EditControl(this.map);
				break;

			case "edit_rotate":
				this.edit = new EditRotate(this.map);
				break;

			case "edit_scale":
				this.edit = new EditScale(this.map);
				break;

			default:
				this.edit = new EditControl$1(this.map);
				break;
		}

		this.bindEvents();
	}
	bindEvents() {
		this.edit.bindEvents(this.updateFeatureLayerFunc);
	}
	destroy() {
		if (!this.edit) return;
		this.edit.destroy();
		this.edit = null;
		this.mode = null;
	}
	updateEditSource(data) {
		this.edit.updateEditSource(data);
	}
}

const DEFAULT_COLOR = "red";

const DEFAULT_WIDTH = 2;

// FILL
const FILL_PAINT = {
	fillColor: DEFAULT_COLOR,
	fillOpacity: 0.1,
};

// LINE
const LINE_PAINT = {
	strokeColor: DEFAULT_COLOR,
	strokeWidth: DEFAULT_WIDTH,
	dashArray: [1, 0],
	dashOffset: 0,
};

const LINE_LAYOUT = {
	lineCap: "round",
	lineJoin: "round",
};

// POINT
const POINT_PAINT = {
	circleColor: DEFAULT_COLOR,
	circleRadius: 5,
	circleOpacity: 0.5,
	"circle-stroke-color": "#fff",
	"circle-stroke-width": DEFAULT_WIDTH,
	circleStrokeOpacity: 1,
};

// TEXT
const TEXT_PAINT = {
	color: "#D20C0C",
	haloColor: "#fff",
	haloWidth: DEFAULT_WIDTH,
};

const TEXT_LAYOUT = {
	lable: "自定义文本",
	size: 16,
	rotate: 0,
	// "text-offset": [0, 0.6],
	// "text-anchor": "top",
	// "text-font": ["Open Sans Regular"],
};

// ICON
const ICON_PAINT = {
	opacity: 1,
	haloColor: DEFAULT_COLOR,
	haloWidth: DEFAULT_WIDTH,
};

const ICON_LAYOUT = {
	iconPath: "draw-icon",
	size: 1,
	offset: [0, -20],
};

const ICON_MODES = ["draw_icon"];

function generateIconLayer({ map, mode, features }) {
	features.forEach((feature) => {
		feature.properties = {
			...ICON_LAYOUT,
			...ICON_PAINT,
			...feature.properties,
		};
	});

	const iconLayer = {
		id: `${mode}-${features[0].id}-icon`,
		type: "symbol",
		source: {
			type: "geojson",
			data: {
				type: "FeatureCollection",
				features,
			},
		},
		layout: {
			"icon-image": ["get", "iconPath"],
			"icon-size": ["get", "size"],
			"icon-offset": ["get", "offset"],
		},
		paint: {
			"icon-opacity": ["get", "opacity"],
			"icon-halo-color": ["get", "haloColor"],
			"icon-halo-width": ["get", "haloWidth"],
		},
	};

	map.addLayer(iconLayer);

	return [iconLayer.id];
}

function isIconMode(mode) {
	return ICON_MODES.includes(mode);
}

const LINE_MODES = ["draw_line_string", "draw_fine_arrow"];

function generateLineLayer({ map, mode, features }) {
	features.forEach((feature) => {
		feature.properties = {
			...LINE_PAINT,
			...LINE_LAYOUT,
			...feature.properties,
		};
	});

	const lineLayer = {
		id: `${mode}-${features[0].id}-line`,
		type: "line",
		source: {
			type: "geojson",
			data: {
				type: "FeatureCollection",
				features,
			},
		},
		layout: {
			"line-cap": ["get", "lineCap"],
			"line-join": ["get", "lineJoin"],
		},
		paint: {
			"line-color": ["get", "strokeColor"],
			"line-width": ["get", "strokeWidth"],
			"line-dasharray": ["get", "dashArray"],
		},
	};

	map.addLayer(lineLayer);

	return [lineLayer.id];
}

function isLineMode(mode) {
	return LINE_MODES.includes(mode);
}

const PT_MODES = ["draw_point"];

function generatePointLayer({ map, mode, features }) {
	features.forEach((feature) => {
		feature.properties = {
			...POINT_PAINT,
			...feature.properties,
		};
	});

	const circleLayer = {
		id: `${mode}-${features[0].id}-circle`,
		type: "circle",
		source: {
			type: "geojson",
			data: {
				type: "FeatureCollection",
				features,
			},
		},
		paint: {
			"circle-color": ["get", "circleColor"],
			"circle-radius": ["get", "circleRadius"],
			"circle-opacity": ["get", "circleOpacity"],
			"circle-stroke-color": ["get", "circleStrokeColor"],
			"circle-stroke-width": ["get", "circleStrokeWidth"],
			"circle-stroke-opacity": ["get", "circleStrokeOpacity"],
		},
	};

	map.addLayer(circleLayer);

	return [circleLayer.id];
}

function isPointMode(mode) {
	return PT_MODES.includes(mode);
}

const PLG_MODES = [
	"draw_attack_arrow",
	"draw_double_arrow",
	"draw_three_arrow",
	"draw_circle",
	"draw_ellipse",
	"draw_rectangle",
	"draw_polygon",
	"draw_gathering_place",
	"draw_straight_arrow",
];

function generatePolygonLayer({ map, mode, features }) {
	features.forEach((feature) => {
		feature.properties = {
			...FILL_PAINT,
			...LINE_PAINT,
			...LINE_LAYOUT,
			...feature.properties,
		};
	});

	const fillLayer = {
		id: `${mode}-${features[0].id}-fill`,
		type: "fill",
		source: {
			type: "geojson",
			data: {
				type: "FeatureCollection",
				features,
			},
		},
		paint: {
			"fill-color": ["get", "fillColor"],
			"fill-opacity": ["get", "fillOpacity"],
		},
	};

	const strokeLayer = {
		id: `${mode}-${features[0].id}-line`,
		type: "line",
		source: {
			type: "geojson",
			data: {
				type: "FeatureCollection",
				features,
			},
		},
		layout: {
			"line-cap": ["get", "lineCap"],
			"line-join": ["get", "lineJoin"],
		},
		paint: {
			"line-color": ["get", "strokeColor"],
			"line-width": ["get", "strokeWidth"],
			"line-dasharray": ["get", "dashArray"],
		},
	};

	map.addLayer(fillLayer);
	map.addLayer(strokeLayer);

	return [fillLayer.id, strokeLayer.id];
}

function isPolygonMode(mode) {
	return PLG_MODES.includes(mode);
}

const TEXT_MODES = ["draw_text"];

function generateTextLayer({ map, mode, features }) {
	features.forEach((feature) => {
		feature.properties = {
			...TEXT_PAINT,
			...TEXT_LAYOUT,
			...feature.properties,
		};
	});

	const textLayer = {
		id: `${mode}-${features[0].id}-text`,
		type: "symbol",
		source: {
			type: "geojson",
			data: {
				type: "FeatureCollection",
				features,
			},
		},
		layout: {
			"text-field": ["get", "label"],
			"text-size": ["get", "size"],
			"text-rotate": ["get", "rotate"],
			// "text-offset": [0, 0.6],
			// "text-anchor": "top",
			// "text-font": ["Open Sans Regular"],
		},
		paint: {
			"text-color": ["get", "color"],
			"text-halo-color": ["get", "haloColor"],
			"text-halo-width": ["get", "haloWidth"],
		},
	};

	// const circleLayer = {
	//   id: `${mode}-${features[0].id}-circle`,
	//   type: "circle",
	//   source: {
	//     type: "geojson",
	//     data: {
	//       type: "FeatureCollection",
	//       features,
	//     },
	//   },
	//   filter: ["all", ["==", "invalid", "true"]],
	//   paint: {
	//     "circle-radius": 5, // DrawUtits.DefaultPointSize,
	//     "circle-color": "#fbb03b", // DrawUtits.DefaultPointColor,
	//   },
	// };

	map.addLayer(textLayer);
	// map.addLayer(circleLayer);

	return [textLayer.id];
}

function isTextMode(mode) {
	return TEXT_MODES.includes(mode);
}

// params = {map, mode, features}
function renderFeatureLayer(params) {
	const { mode } = params;
	const modeType = getModeType(mode);

	let layerIds = [];

	switch (modeType) {
		case "point":
			layerIds = generatePointLayer(params);
			break;
		case "line":
			layerIds = generateLineLayer(params);
			break;
		case "polygon":
			layerIds = generatePolygonLayer(params);
			break;
		case "text":
			layerIds = generateTextLayer(params);
			break;
		case "icon":
			layerIds = generateIconLayer(params);
	}

	return layerIds;
}

function updateFeatureLayer({ map, mode, layerIds, points }) {
	layerIds.forEach((layerId) => {
		const source = map.getSource(layerId);

		if (source) {
			const geojson = source._data;
			// console.log("geojson", geojson);

			const coords = GenerateDict[mode](points);
			const feature = geojson.features[0];
			feature.geometry.coordinates = coords;
			geojson.features.forEach((feature) => {
				feature.geometry.coordinates = coords;
			});
			source.setData(geojson);
		}
	});
}

// mapbox gl 绘制属性参考
// https://blog.csdn.net/weixin_48189070/article/details/125440455
// Layer properties 参考
// https://docs.mapbox.com/style-spec/reference/layers#layer-properties
// 样式数据保存在前端
// export function updateFeatureLayerStyles({ map, layerIds, styles }) {
//   // console.log("updateFeatureLayerStyles", layerIds, styles);
//   layerIds.forEach((layerId) => {
//     Object.entries(styles).forEach(([key, value]) => {
//       // 图层类型--fill/line/symbol
//       const isSame = layerId.includes(key.split("-")[0]);
//       if (isSame) {
//         const layout = ["text-field", "text-size", "icon-size"];
//         if (layout.includes(key)) {
//           map.setLayoutProperty(layerId, key, value);
//         } else {
//           map.setPaintProperty(layerId, key, value);
//         }
//       }
//     });
//   });
// }

// 样式数据保存在 geojson
function updateFeatureLayerStyles({ map, layerIds, styles }) {
	// console.log("updateFeatureLayerStyles", layerIds, styles);
	layerIds.forEach((layerId) => {
		const source = map.getSource(layerId);
		const geojson = source._data;
		// console.log(geojson);

		geojson.features.forEach((feature) => {
			Object.entries(styles).forEach(([key, value]) => {
				feature.properties[key] = value;
			});
		});
		source.setData(geojson);
	});
}

function getModeType(mode) {
	if (isPointMode(mode)) return "point";
	if (isLineMode(mode)) return "line";
	if (isPolygonMode(mode)) return "polygon";
	if (isTextMode(mode)) return "text";
	if (isIconMode(mode)) return "icon";
}

var drawIconSrc = "";

var rotateIconSrc = "";

var scaleIconSrc = "";

class MapboxDraw2 {
  constructor({ map, canEdit }) {
    this.map = map;
    this.canEdit = canEdit || true;
    this.draw = null;
    this.store = {};
    this.mode = null;
    this.edit = null;
    this.clickFunc = null;
    this.selectedIds = [];

    this.events = [];

    this.init();
  }

  init() {
    const draw = new MapboxDraw({
      displayControlsDefault: false,
      userProperties: true,
      modes: {
        ...MapboxDraw.modes,
        ...MapboxDrawExtends,
      },
    });
    this.draw = draw;

    this.map.addControl(draw);

    this.registerIcon({ iconName: "draw-icon", iconUrl: drawIconSrc });

    if (this.canEdit) {
      this.registerIcon({ iconName: "rotate-icon", iconUrl: rotateIconSrc });
      this.registerIcon({ iconName: "scale-icon", iconUrl: scaleIconSrc });

      // 编辑之后更新回调函数
      const updateFeatureLayerFunc = this.updateFeatureLayerById.bind(this);
      this.edit = new MapboxEdit(this.map, { updateFeatureLayerFunc });
    }
  }

  bindEvents(eventName, callback) {
    if (eventName === "draw.create") {
      const func = this.onCreateComplete.bind(this, callback);
      this.map.on("draw.create", func);

      this.events.push({
        eventName: "draw.create",
        func: func,
      });
    }

    if (eventName === "draw.select") {
      this.map.on("draw.select", callback);

      this.events.push({
        eventName: "draw.select",
        func: callback,
      });
    }

    this.clickFunc = this.onClickFeature.bind(this);
    this.map.on("click", this.clickFunc);

    this.events.push({
      eventName: "click",
      func: this.clickFunc,
    });
  }

  registerIcon({ iconName, iconUrl }) {
    const map = this.map;
    // Add a stretchable image that can be used with `icon-text-fit`
    // In this example, the image is 600px wide by 400px high.
    map.loadImage(iconUrl, (error, image) => {
      if (error) throw error;
      if (!map.hasImage(iconName)) {
        map.addImage(iconName, image, {
          // content: [16, 16, 300, 384], // place text over left half of image, avoiding the 16px border
          // stretchX: [[16, 584]], // stretch everything horizontally except the 16px border
          // stretchY: [[16, 384]], // stretch everything vertically except the 16px border
        });
      }
    });
  }

  changeMode(mode) {
    if (mode.includes("edit")) {
      // edit_control edit_pan edit_rotate edit_scale
      this.changeEditMode(mode);
    } else {
      // draw_text draw_icon draw_circle draw_ellipse
      // draw_rectangle draw_polygon draw_line_string
      // draw_attack_arrow draw_double_arrow draw_three_arrow
      this.changeDrawMode(mode);
    }
  }

  changeDrawMode(mode) {
    // mode
    // draw_text draw_icon draw_circle draw_ellipse
    // draw_rectangle draw_polygon draw_line_string
    // draw_attack_arrow draw_double_arrow draw_three_arrow
    this.draw.changeMode(mode);
    this.mode = mode;

    // 绘制时需要解除元素查询绑定
    this.map.off("click", this.clickFunc);
  }

  changeEditMode(mode) {
    // 未触发绘制事件,暂时不切换
    // if (this.mode !== "simple_select") {
    //   this.changeDrawMode("simple_select");
    // }

    this.edit.changeMode(mode);
  }

  getModeTypeById(featureId) {
    if (!featureId) return;

    if (!this.store[featureId]) return;

    const mode = this.store[featureId].mode;
    return getModeType(mode);
  }

  onCreateComplete(callback, evt) {
    // console.log("onCreateComplete--call", evt, callback);
    // 移除默认的绘制要素
    const featureId = evt.features[0].id;
    this.draw.delete(featureId);

    // 绘制自定义要素
    const params = {
      map: this.map,
      mode: this.mode,
      features: evt.features,
    };
    const layerIds = renderFeatureLayer(params);

    // 存储自定义要素信息--mode layerIds
    evt.mode = this.mode;
    evt.layerIds = layerIds;
    evt.isNew = true;
    this.store[featureId] = evt;

    // 触发自定义事件
    callback(evt);

    // 绑定点击事件
    if (this.canEdit) {
      // 绘制完成时会立即触发 click 事件，需要延迟绑定
      setTimeout(() => {
        this.map.on("click", this.clickFunc);
      }, 500);
    }
  }

  onClickFeature(evt) {
    //   console.log("click", e);
    const features = this.map.queryRenderedFeatures(evt.point);
    // console.log(features);

    let mode = "";
    if (features.length > 0) {
      const ids = Object.keys(this.store);
      const idsFilter = ids.filter((id) => {
        const feats = features.filter((feat) => feat.source.includes(id));
        if (feats.length > 0) {
          mode = feats[0].source.split("-")[0];
        }
        return feats.length;
      });

      this.selectedIds = idsFilter;

      for (let i = 0; i < idsFilter.length; i++) {
        // 控制点绘制，目前只有箭头有
        const controlPnts = this.store[idsFilter[i]].controlPnts;
        if (controlPnts) {
          this.edit.updateEditSource(controlPnts);
          break;
        }
      }
    } else {
      this.selectedIds = [];
      this.edit.updateEditSource([]);
    }

    // 借用 mapbox 事件系统，注册选中事件
    this.map.fire("draw.select", { selectedIds: this.selectedIds, mode });
  }

  deleteFeatureLayerById(featureId) {
    // const featureId = this.selectedIds[0];
    const { layerIds } = this.store[featureId];
    layerIds.forEach((layerId) => {
      this.map.removeLayer(layerId);
      this.map.removeSource(layerId);
    });

    if (this.canEdit) {
      this.edit.updateEditSource([]);
    }

    delete this.store[featureId];
  }

  addFeatureLayer(geojson) {
    const map = this.map;
    const properties = geojson.properties;
    // 绘制自定义要素
    const params = {
      map: map,
      mode: properties.mode,
      features: geojson.features,
    };
    const layerIds = renderFeatureLayer(params);
    updateFeatureLayerStyles({ map, layerIds, styles: properties });

    // 存储自定义要素信息--mode layerIds
    const evt = {
      controlPnts: properties.controlPnts,
      mode: properties.mode,
      layerIds,
      features: geojson.features,
      id: properties.itemId,
    };

    this.store[geojson.features[0].id] = evt;
  }

  updateFeatureLayerById(points) {
    // console.log("updateFeatureLayerById", this.selectedIds, points);
    const map = this.map;
    const featureId = this.selectedIds[0];
    const { layerIds, mode } = this.store[featureId];
    this.store[featureId].controlPnts = points;
    updateFeatureLayer({ map, layerIds, points, mode });
  }

  updateFeatureLayerStylesById({ featureId, styles }) {
    const map = this.map;
    const layerIds = this.store[featureId].layerIds;
    updateFeatureLayerStyles({ map, layerIds, styles });
  }

  clearFeatureLayer() {
    const ids = Object.keys(this.store);
    ids.forEach((id) => {
      this.deleteFeatureLayerById(id);
    });
  }

  destroy() {
    this.events.forEach((eventName, func) => {
      this.map.off(eventName, func);
    });

    this.map.removeControl(this.draw);

    this.draw = null;
    this.store = {};
    this.mode = null;
    this.edit = null;
    this.clickFunc = null;
    this.selectedIds = [];
  }
}

export { MapboxDraw2 as default };
